import 'dart:convert';
import 'dart:typed_data';

import 'package:commons/commons.dart' hide FormData, Response;
import 'package:dio/dio.dart';
import 'package:dio_service/src/config/config.dart';
import 'package:dio_service/src/res/result_data.dart';
import 'package:dio_service/src/service/remote_service.dart';
import 'package:ui/ui.dart';

import '../config/code.dart';

class RemoteServiceImp implements RemoteService {
  RemoteServiceImp(this.config);

  ///[needForm] 是否是表单格式  default false
  ///[loadding] is need loading default false
  @override
  Future<NetRes<T>> get<T>(
    String path, {
    final Map<String, dynamic>? args,
    final bool needForm = false,
    final bool loadding = false,
    final Map<String, dynamic>? headers,
    final T Function(Map<String, dynamic> json)? convert,
  }) {
    return request(
      path,
      isPost: false,
      args: args,
      needForm: needForm,
      loadding: loadding,
      headers: headers,
      convert: convert,
    );
  }

  ///[needForm] 是否是表单格式  default false
  ///[loadding] is need loading default true
  @override
  Future<NetRes<T>> post<T>(
    String path, {
    final Map<String, dynamic>? args,
    final bool needForm = false,
    final bool loadding = false,
    final Map<String, dynamic>? headers,
    final T Function(Map<String, dynamic> json)? convert,
  }) {
    return request(
      path,
      args: args,
      needForm: needForm,
      loadding: loadding,
      headers: headers,
      convert: convert,
    );
  }

  @override
  Future<NetRes<T>> upload<T>(
    String path, {
    required final FormData data,
    final Map<String, dynamic>? args,
    final bool loadding = false,
    final Map<String, dynamic>? headers,
    final T Function(Map<String, dynamic> json)? convert,
    final ProgressCallback? onSendProgress,
  }) {
    return request(
      path,
      data: data,
      args: args,
      loadding: loadding,
      headers: headers,
      convert: convert,
      onSendProgress: onSendProgress,
    );
  }

  @override
  Future<NetRes> download<T>(
    String path,
    final String savePath, {
    final void Function(int p1, int p2)? onReceiveProgress,
    final Map<String, dynamic>? args,
  }) async {
    Response response = await _dio.download(
      path,
      savePath,
      onReceiveProgress: onReceiveProgress,
      queryParameters: args,
      // options: options,
    );
    if (response.statusCode == 200) {
      return NetRes(code: NetResponseCode.success);
    } else {
      return NetRes(code: NetResponseCode.networkError);
    }
  }

  @override
  Future<int> getFileSizeFromUrl(String url) async {
    final response = await _dio.head(url);
    if (response.statusCode == 200) {
      // 获取Content-Length头部来确定文件大小
      final contentLength = response.headers['content-length'];
      if (contentLength != null) {
        return int.parse(contentLength.first);
      }
    }
    return 0; // 文件大小获取失败
  }

  @override
  Future<Uint8List?> getImage(String url) async {
    final response = await _dio.get(
      url,
      options: Options(responseType: ResponseType.bytes),
    );
    return response.data as Uint8List?;
  }

  /*
   * 取消请求
   *
   * 同一个cancel token 可以用于多个请求，当一个cancel token取消时，所有使用该cancel token的请求都会被取消。
   * 所以参数可选
   */
  void cancelRequest({CancelToken? token}) {
    token ?? cancelToken.cancel('canceled');
  }

  Response _resoveError(dynamic error) {
    var dr = Response(requestOptions: RequestOptions());

    if (error is DioException) {
      DioException exception = error;
      ILog.d('exception.response.data----${exception.response?.data}');
      ILog.d(exception.toString());
      return exception.response ?? dr;
    }
    return dr;
  }

  Dio get _dio => config.dio;
  CancelToken cancelToken = CancelToken();

  final ServiceConfig config;
}

extension _RemoteServiceImp on RemoteServiceImp {
  Future<NetRes<T>> request<T>(
    String path, {
    final dynamic args,
    final bool isPost = true,
    final bool loadding = true,
    final bool needForm = false,
    final FormData? data,
    Map<String, dynamic>? headers,
    final ProgressCallback? onSendProgress,
    final ProgressCallback? onReceiveProgress,
    final T Function(Map<String, dynamic> json)? convert,
  }) async {
    if (loadding) {
      Hud.loading();
    }
    if (!path.startsWith("http")) {
      path = config.baseUrl + path;
    }
    headers ??= {};
    if (data != null) {
      headers[Headers.contentLengthHeader] = data.length;
    }
    Response response = isPost
        ? await _dio
            .post(
            path,
            data: needForm ? (FormData.fromMap(args ?? {})) : args,
            options: Options(
              headers: headers,
              sendTimeout: onSendProgress != null
                  ? const Duration(minutes: 10)
                  : const Duration(seconds: 10),
              receiveTimeout: onReceiveProgress != null
                  ? const Duration(minutes: 10)
                  : const Duration(seconds: 10),
            ),
            cancelToken: cancelToken,
          )
            .catchError((e) {
            return _resoveError(e);
          })
        : await _dio
            .get(path, queryParameters: args, cancelToken: cancelToken)
            .catchError((e) {
            return _resoveError(e);
          });
    if (loadding) {
      Hud.hide();
    }

    if (response.statusCode == 200) {
      Map<String, dynamic> data;
      if (response.data is String) {
        data = jsonDecode(response.data);
      } else {
        data = response.data;
      }
      var res = NetRes.fromJson(data, convert);
      if (!res.isSuccess) {
        Hud.toast(res.msg ?? '未知错误');
      }
      //TODO: invalidToken
      // if (res.code == NetResponseCode.invalidToken &&
      //     Get.currentRoute != RouterPath.login) {
      //   Get.offAllNamed(RouterPath.login);
      // }
      return res;
    } else {
      ILog.d(response.toString());
      Hud.toast('网络开小差了，请稍后重试');
      return NetRes(code: NetResponseCode.networkError);
    }
  }
}
